Construção de compiladores
34 pág.

Construção de compiladores


DisciplinaConstrução de Compilares3 materiais14 seguidores
Pré-visualização10 páginas
seqüência de comandos em uma linguagem intermediária, cuja
forma mais comum é a de quádruplas, ou seja, de comandos compostos de um operador e de três operandos, que
quase sempre são endereços. Por exemplo, o comando de atribuição:
x: = (a+b) * c
Tradução dirigida por sintaxe 19
Árvores Sintáticas
A primeira árvore que se poderia usar como base para uma representação intermediária é a árvore de derivação
correspondente ao programa fonte, de acordo com a gramática da linguagem fonte usada na construção do analisador
sintático. Entretanto, essa árvore de derivação em geral não é conveniente, uma vez que inclui muitos detalhes
desnecessários, que foram incluídos na sintaxe da linguagem fonte para resolver problemas de ambigüidade,
precedência, pontuação, clareza, legibilidade, etc. Por essa razão, em alguns casos é conveniente a definição de uma
segunda sintaxe, chamada sintaxe abstrata, que ignora todos os aspectos não essenciais da sintaxe original, conhecida
como sintaxe concreta.
Análise semântica
Chama-se análise semântica a terceira fase da compilação, onde se verificam os erros de semântica no código fonte
e se faz a coleta das informações necessárias para a próxima fase da compilação, a geração de código objeto. O
objetivo da análise semântica é trabalhar no nível de inter-relacionamento entre partes distintas do programa[1].
Objetivo da Análise Semântica
A análise sintática verifica e aponta as expressões que quebram qualquer regra determinada pela gramática. Mas o
nível de complexidade aumenta quando tratamos de liguagens dependentes de contexto. A análise semântica busca
apontar (não resolver) este tipo de erros (dependentes de contexto).
O objetivo da análise semântica é, assim, criar, a partir de um texto-fonte, uma interpretação expressa em alguma
notação adequada - geralmente uma linguagem intermediária do compilador. Isto é feito com base nas informações
das tabelas e nas saídas dos outros analisadores. Denomina-se semântica de uma sentença o significado por ela
assumido dentro do contexto em que se encontra. Semântica de uma linguagem é a interpretação que se pode atribuir
ao conjunto de todas as suas sentenças.
A análise semântica utiliza-se da verificação de tipos para verificar se um determinado operando recebe outro
operando de mesmo tipo. Um exemplo comum nas linguagens de programação é a análise semântica retornar um
erro quando uma variável do tipo numérica (real ou inteira) receber um valor tipo texto (string).
Um compilador usa uma tabela de símbolos (TS) para guardar informações sobre os nomes declarados em um
programa. A TS é pesquisada cada vez que um nome é encontrado no programa fonte. Alterações são feitas na TS
sempre que um novo nome ou nova informação sobre um nome já existente é obtida. A gerência da TS de um
compilador deve ser implementada de forma a permitir inserções e consultas da forma mais eficiente possível, além
de permitir o crescimento dinâmico da mesma.Cada entrada na ts é a declaração de um nome. Cada entrada na ts
pode ser implementada como um registro ( record ou struct) contendo campos (nome, tipo, classe, tamanho, escopo,
etc.) que a qualificam.
Exemplos típicos de erros semânticos são:
\u2022\u2022 Uma variável não declarada
\u2022\u2022 Uma multiplicação entre tipos de dados diferentes
\u2022\u2022 Atribuição de um literal para outro tipo, como um inteiro em uma string ou vice-versa.
Análise semântica 20
Checagem de Tipo
O processo de verificação dos tipos ocorre em tempo de compilação (checagem estática) ou em tempo de execução
(checagem dinâmica). As linguagens de programação conforme a especificação da linguagem podem ser referidas
como de tipagem forte e tipagem fraca, influenciando o processo de checagem de tipo.
Tipagem estática
É quando acontece durante a copilação, pode ser por meio de alocação, desalocação de variaveis locais ou descoberta
de tipos em expressões numericas.
Tipagem dinâmica
É quando acontece durante a execução, pode ser por meio de alocação na heap com malloc (em C), verificação de
cast de classe (em java), ou verificação de limites de arrays (em java).
Bibliografia e referências
Referências
[1] http:/ / www. dca. fee. unicamp. br/ cursos/ EA876/ apostila/ HTML/ node71. html
Bibliografia
\u2022 Análise semântica- Programação de Sistemas- Ivan Luiz Marques Ricarte (http:/ / www. dca. fee. unicamp. br/
cursos/ EA876/ apostila/ HTML/ node71. html)
\u2022 Uso de Prolog no Desenvolvimento de Compiladores - Arquiteturas Especiais de Computadores - André Luis
Martinotto (http:/ / www. inf. ufrgs. br/ procpar/ disc/ cmp135/ trabs/ martinotto/ trabII/ semantica. htm)
Esta página é somente um esboço.
Ampliando-a você ajudará a melhorar o Wikilivros.
Geração de código intermediário 21
Geração de código intermediário
Tomando um compilador como um modelo de análise-e-síntese, podemos considerar que seus módulos iniciais
("front-end modules") traduzem um programa fonte em uma representação (linguagem) intermediária, a partir da
qual seus módulos finais ("back-end modules") geram o código objeto final.
A geração de um código intermediário requer um passo a mais para a tradução, tornando o processo um pouco mais
lento. Embora um programa fonte possa ser traduzido diretamente para a linguagem objeto, o uso de uma
representação intermediária, independente de máquina, tem as seguintes vantagens:
1.1. Reaproveitamento de código, facilitando o transporte de um compilador para diversas plataformas de hardware;
somente os módulos finais precisam ser refeitos a cada transporte.
2.2. Um otimizador de código independente de máquina pode ser usado no código intermediário.
Pode-se pensar nessa representação intermediária como um programa para uma máquina abstrata. A representação
intermediária deve possuir duas propriedades importantes: ser fácil de produzir e fácil de traduzir no programa alvo.
A diferença básica entre o código intermediário e o código objeto final é que não são especificados detalhes da
máquina alvo, tais como quais registradores serão usados, quais endereços de memória serão referenciados, etc.
Tipos de código intermediário
\u2022 HIR \u2013 High Intermediate Representation
\u2022\u2022 Usada nos primeiros estágios do compilador
\u2022\u2022 Simplificação de construções gramaticais para somente o essencial para otimização/geração de código
\u2022 MIR \u2013 Medium Intermediate Representation
\u2022\u2022 Boa base para geração de código eficiente
\u2022\u2022 Pode expressar todas características de linguagens de programação de forma independente da linguagem
\u2022\u2022 Representação de variáveis, temporários, registradores
\u2022 LIR \u2013 Low Intermediate Representation
\u2022\u2022 Quase 1-1 para linguagem de máquina
\u2022\u2022 Dependente da arquitetura
Representação
As formas de representação mais comuns dos tipos de códigos intermediários são:
\u2022\u2022 HIR
\u2022\u2022 Árvore e grafo de sintaxe
\u2022\u2022 Notações Pós-fixada e Pré-fixada
\u2022\u2022 Representações linearizadas
\u2022\u2022 MIR
\u2022\u2022 Árvore e grafo de sintaxe
\u2022\u2022 Notações Pós-fixada e Pré-fixada:
\u2022\u2022 Representações linearizadas
\u2022\u2022 Código de três endereços:
\u2022\u2022 Quádruplas
\u2022\u2022 Triplas
\u2022\u2022 Grafos acíclicos dirigidos (DAG)
\u2022\u2022 LIR
Geração de código intermediário 22
\u2022\u2022 Instruções assembler
Geração de código
A tradução do código de alto nível para o código do processador está associada a traduzir para a linguagem-alvo a
representação da árvore gramatical obtida para as diversas expressões do programa. Embora tal atividade possa ser
realizada para a árvore completa após a conclusão da análise sintática, em geral ela é efetivada através das ações
semânticas associadas à aplicação das regras de reconhecimento do analisador sintático. Este procedimento é
denominado tradução dirigida pela sintaxe.
Em geral, a geração de código não se dá diretamente para a linguagem assembly do processador-alvo. Por
conveniência, o analisador sintático gera código para uma máquina abstrata, com uma linguagem próxima ao
assembly, porém independente de processadores