Introdução à Compilação
Aula 8 – Análise Semântica Prof. Nemésio Freitas Duarte Filho
Estrutura geral de um compilador
programa-fonte analisador léxico
analisador sintático
analisador semântico
gerador de código intermediário
otimizador de código gerador de código programa-alvo Tabela de símbolos Tabela de palavras e símbolos reservados Manipulação de erros dados de entrada saída
3
Introdução
Objetivos
Responsável pela corretude do programa com relação às regras da linguagem que não foram descritas
pela linguagem.
Verificar se o programa está de acordo com as restrições contextuais da linguagem fonte
Verificação pode ser
Estática: em tempo de compilação Dinâmica: em tempo de execução
4
Introdução
Tipos de Verificações Estáticas
Verificação de tipos: aplicação de um operador a um
operando incompatível. Ex: soma de variáveis de tipos incompatíveis, atribuição de expressões incompatíveis com variáveis
Verificações de fluxo de controle: uso de goto e break sem ter um local para onde transferir o crontole da execução. Ex: usar break fora de while/for/switch
Verificações de unicidade: declarar
5
Introdução
Sistemas de Tipos
Coleção de regras para atribuir expressões de tipo a várias partes de um programa
Verificação de tipos pode sempre ser feita dinamicamente, se junto com os dados existirem sempre informações de tipos Uma linguagem é dita fortemente tipada se ela garante que
erros de tipo não podem ocorrer durante a execução Na prática, algumas verificações só podem ser feitas
6
Introdução
Exemplo de Verificações Semânticas
Tipo dos operandos de uma expressão são compatíveis com tipo do operador?
Só arrays são indexados?
Número e tipo de argumentos passados a uma função estão corretos?
7
Introdução
Exemplo de Verificações Semânticas
Identificador usado num comando está declarado? Identificador está declarado apenas uma vez no
mesmo módulo do programa?
Qual o escopo de um identificador?
Os tipos dos dois lados de um comando de atribuição são compatíveis?
8
Introdução
Exemplo de Verificações Semânticas
A variável de controle de um comando repetitivo é inteira?
Uma variável é inicializada e referenciada pelo menos uma vez num programa?
9
Introdução
Exemplo de Verificações Semânticas
A linguagem admite recursão? Uma função ou procedimento é recursivo?
Uma alteração no valor de uma variável tem alguma utilidade?
O nome de chamada de um procedimento está declarado corretamente?
10
O que é a semântica de um programa?
Sintaxe
Como o programa é constituído
Representação textual ou estrutura
Semântica
11
Qual o motivo da análise semântica?
Certifica-se de que o programa está de acordo
com as definições da linguagem de programação
Reportar, sempre que haja erros semânticos,
mensagens de erro que sejam úteis para o utilizador
Não é preciso muito trabalho adicional se for
incorporada durante a criação da representação intermédia
12
Erros na Análise Semântica
boolean sum(int A[], int N) {
Int i, sum;
For(i=0; i<N; i++) { sum1 = sum + A[i]; } return sum; } ... Int s = sum(A); Falta um argumento
Não foi atribuído valor inicial a sum
Tipo da variável devolvida não confere com a declaração do cabeçalho da função
Análise semântica
Função: verificação do uso adequado
Análise contextual: declarações prévias de variáveis,
procedimentos, etc.
Checagem de tipos
Coisas que vão além do domínio da sintaxe
Sensitividade ao contexto!
Tipos de análise semântica
Estática, em tempo de compilação: linguagens
tipadas, que exigem declarações
C, Pascal, etc.
Dinâmica, em tempo de execução: linguagens em
que as variáveis são determinadas pelo contexto de uso
Análise semântica
Devido às variações de especificação
semântica das linguagens de programação, a análise semântica
Não é tão bem formalizada
Não existe um método ou modelo padrão de
representação do conhecimento
Não existe um mapeamento claro da representação
para o algoritmo correspondente
Análise é artesanal, dependente da linguagem
Análise semântica
Semântica dirigida pela sintaxe
Conteúdo semântico fortemente relacionado à
sintaxe do programa
Maioria das linguagens de programação modernas
Em geral, a semântica de uma linguagem de
programação não é especificada
O projetista do compilador tem que analisar e
Análise semântica
Em geral, a gramática de atributos de uma
gramática especifica
Comportamento semântico das operações Checagem de tipos
Manipulação de erros Tradução do programa
Tabela de símbolos: estrutura essencial
Tabela de símbolos
Captura a sensitividade ao contexto e as
ações executadas no decorrer do programa
Atrelada a todas as etapas da compilação
Permite a realização da análise semântica
Tabela de símbolos
Permite saber durante a compilação de um
programa o tipo e o valor de seus elementos (números e identificadores), escopo destes, número e tipo dos parâmetros de um
procedimento, etc.
Cada token tem atributos/informações diferentes
associadas
Cadeia Token Categoria Tipo Valor ...
i id var integer 1 ...
fat id proc - - ...
2 num - integer 2 ...
Tabela de símbolos
Exemplo de atributos de identificador de
variável
Tipo de variável (inteira, real, etc.), nome da
variável, endereço na memória, escopo (global, local, etc.), etc.
Para vetores, ainda seriam necessários atributos
Tabela de símbolos
Principais operações efetuadas
Inserir: armazena na tabela informações fornecidas
pelas declarações no programa
Busca: recupera da tabela informações de um
elemento declarado no programa quando esse elemento é utilizado
Remover: remove (ou torna inacessível) da tabela
informações sobre um elemento declarado que não se mostra mais necessário no programa
As especificidades dessas operações são
dependentes da linguagem de programação em questão
Tabela de símbolos
A tabela é acessada pelo compilador sempre
que um elemento é mencionado no programa
Verificar ou incluir sua declaração
Verificar seu tipo, seu escopo ou alguma outra
informação
Atualizar alguma informação associada ao
identificador (p.e., valor)
Remover um elemento quando este não se faz mais
Tabela de símbolos
Estrutura da tabela de símbolos:
determinada pela eficiência das operações
de inserir, verificar e remover
Várias possibilidades
Implementação
Estática
Dinâmica (Melhor opção)
Estrutura
Listas lineares
Árvores de busca (por exemplo, B e AVL) Hashing
Tabela de símbolos
Questões de projeto
Tamanho da tabela: tipicamente, de algumas
centenas a mil campos
Dependente da forma de implementação
Na implementação dinâmica, não é necessário se preocupar
com isso
Uma única tabela para todas as declarações ou
várias tabelas, sendo uma para cada tipo de
declaração (constantes, variáveis, tipos, procedimentos e funções)
Diferentes declarações têm diferentes
informações/atributos
por exemplo, variáveis não têm número de argumentos,
Tabela de símbolos
Representação de escopo de identificadores do
programa
Várias tabelas ou uma única tabela com a
identificação do escopo (como um atributo) para cada identificador
Tratamento de escopo
Inserção de identificadores de mesmo nome, mas em níveis
diferentes
Remoção de identificadores cujos escopos deixaram de
existir
A tabela de símbolos pode ser utilizada para
armazenar as palavras reservadas e símbolos
especiais da linguagem, podendo dispensar o uso da tabela de palavras e símbolos reservados
Tabela de símbolos
Descritores
Registros (campos) que formam a tabela de
símbolos
Diferentes identificadores têm diferentes
descritores
Tem que se levar isso em consideração no projeto
Tabela de símbolos
Inserção de elementos na tabela
Associação de regras semânticas às regras gramaticais Verificar se o elemento já não consta na tabela
Busca de informação na tabela
Realizada antes da inserção
Busca de informações para análise semântica
Remoção de elementos da tabela
Tornar inacessíveis dados que não são mais necessários
por exemplo, após o escopo ter terminado
Tabela de símbolos
program id corpo .
programa
inserir(cadeia,token=“id”,cat=”nome_prog”)
Cadeia Token Categoria Tipo Valor ...
meu_prog id nome_prog - - ...
program meu_prog ...
Inserção de elementos na tabela
Tabela de símbolos
var id real
declaração de variáveis
inserir(cadeia,token=“id”,cat=”var”)
Cadeia Token Categoria Tipo Valor ...
meu_prog id nome_prog - - ... x id var integer ... y id var integer ... , : integer inserir(tipo=“real”) inserir(tipo=“integer”) var x, y: integer
Obs: não esta sendo ilustrado o controle das
Tabela de símbolos
Exercício: inclua as funções adequadas
procedure id real declaração de procedimentos , : integer id ( ) corpo ;
Obs: não esta sendo ilustrado o controle das
posições na tabela!
Tabela de símbolos
Exercício: inclua as funções adequadas
Cadeia Token Categoria Tipo Valor ...
meu_prog id nome_prog - - ... x id var integer ... y id var integer ... meu_proc id proc - - ... a id par integer ... b id par real ... c id par real ...
Tabela de símbolos
Tratamento de escopo
Como diferenciar variáveis globais de locais
Tratamento de variáveis de mesmo nome, mas de escopos
diferentes
program meu_prog var x, y: integer
procedure meu_proc(x: integer) var y: real begin read(y); x:=x+y end; begin read(y); x:=x*y end.
Tabela de símbolos
Possibilidades para tratamento de escopos
Inclusão de um campo a mais na tabela de
símbolos indicando o nível da variável no programa
Tabelas diferentes para diferentes escopos
Busca de informação
Sempre que um elemento do programa é utilizado
comando e fator
Tabela de símbolos
id fator número real número inteiro ( expressão ) busca(cadeia,token=“id”,cat=“var”)Tabela de símbolos
comando read write ( id ) , := id expressão ( id ) ; busca(cadeia,token=“id”, cat=“var”) busca(cadeia,token=“id”,cat=“proc”) busca(cadeia,token=“id”,cat=“par”) busca(cadeia,token=“id”,cat=“var”)Tratamento semântico
Verificação do uso adequado dos elementos
do programa
Declaração de identificadores
Erro: identificador não declarado ou declarado duas
vezes
Compatibilidade de tipos em comandos
Checagem de tipos
Concordância entre parâmetros formais e atuais,
Tratamento semântico
Declaração de identificadores
Verificado durante a construção da tabela de símbolos
Compatibilidade de tipos
Dependente do contexto
Atribuição: inteiro:=inteiro, real:=inteiro, string:=cadeia de
caracteres
Erro: inteiro:=real
Comandos de repetição: while booleano do..., if booleano
then...
Expressões e tipos esperados pelos operadores:
inteiro+inteiro, real*real, inteiro+real, inteiro/inteiro, booleano and booleano
Erro: inteiro+booleano
Tratamento semântico
Concordância entre parâmetros formais e
atuais, em termos de número, ordem e tipo
Por exemplo, se declarado:
procedure p(var x: integer; var y: real)
Erros
procedure p(x:integer, y:integer) procedure p(y:real, x:integer) procedure p(x:integer)
Tratamento de escopo
Erro: variável local a um procedimento utilizada no
Exercícios
1- Um programador de C++ codificou o seguinte módulo como parte de sua aplicação:
int a;
int umaFuncao() {return a;} void umaFuncao(int v) {a=v;}
int umaFuncao (int v, int b) {a=v+b; return a;}
Após compilar este código com o compilador g++, ele
analisou o código objeto associado com o aplicativo, que apresenta a tabela de símbolos do módulo objeto, e
encontrou as seguintes definições para o nomes das funções.
000000a T _Z9umaFuncaoi 0000018 T _Z9umaFuncaoii 0000000 T _Z9umaFuncaov
Exercícios
a-) Explique, a partir desse exemplo, como esse compilador faz a descoberta (diferenciação) entre os nomes das
funções
b-) Antes de chegar ao programa anterior, o programador
havia tentado compilar, sem sucesso, a seguinte versão de seu código, na qual também utilizava a sobrecarga de nomes de funções
int a;
int umaFuncao() {retunr a;} void umaFuncao(int v) {a=v;}
int uma Funcao (int v) {a=v; retunr a;}
Explique qual foi a causa d problema que o programador encontrou nessa compilação.
Exercícios
2- Um Programa em C++ contém as seguintes declarações de variáveis:
char c = “0”;
int i = 1;
float f = 2.0;
Qual dos seguintes comandos causariam erros detectados na etapa de análise semântica e, nesse caso, qual o tipo de verificação captura o erro?
a-) f % c b-) i + f c-) f * i d-) i := c
Exercícios
3- Existem basicamente dois tipos de análise
semântica: a estática, e dinâmica. Explique resumidamente, apresentando suas vantagens e desvantagens.
Exercícios
4- A estrutura da tabela de símbolos pode ser implementada através de Listas lineares, Árvores de
busca, Hashing, entre outros. Qual é melhor? Explique.
Exercícios
Resposta:
a-) O compilador incorpora ao nome da função o prefixo
_Z9 e um sufixo que é definido pelos tipos dos
parâmetros da função; no exercício, os sufixos usados são i para cada parâmetro do tipo inteiro e v quando não há parâmetros (void).
Outros possíveis sufixos são b (bool), c (char), s (short), l (long), f (float) e d (double).
b-) O tipo de retorno e a sua ordem influencia na definição
e escolha da função pelo compilador. Assim, a segunda e a terceira versão da função geram o mesmo nome na tabela, produzindo um conflito na tabela de símbolos.
Exercícios
2- Um Programa em C++ contém as seguintes declarações de variáveis:
char c = “0”;
int i = 1;
float f = 2.0;
Qual dos seguintes comandos causariam erros detectados na etapa de análise semântica e, nesse caso, qual o tipo de verificação captura o erro?
a-) f % c (F)
b-) i + f (V)
c-) f * i (V)
Exercícios
3- Existem basicamente dois tipos de análise semântica: a estática, e dinâmica. Explique
resumidamente, apresentando suas vantagens e desvantagens. Tipagem estática
É quando acontece durante a compilação, pode ser por meio de alocação, deslocação de variáveis locais ou descoberta de tipos em expressões numéricas.
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).
Vantagens de tipagem estatica:
- Melhor documentação do código pois ele fica mais legível.
- Existem programas de verificação que podem evitar que um código entre em estado de erro que só são possíveis graças à tipagem estática.
Vantagens/Desvantagens de tipagem dinâmica: - Permite decisões de última hora.
- Permite retirar um pouco do peso das decisões do ombro do programador, já que boa parte delas é realizada de última hora, o próprio compilador cuida delas.
( ou seja, no geral o programador escreve menos, pois tem menos detalhes pra se preocupar ) - A decisão dos tipos se realizada durante a execução pode tornar a execução mais lenta, já que é
Exercícios
4- A estrutura da tabela de símbolos pode ser implementada através de Listas lineares, Árvores de
busca, Hashing, entre outros. Qual é melhor? Explique.
Dúvidas
E-mail:
nemesiofreitas@gmail.com
Site: