Introdução à Compilação
Aula 9 – Erros léxicos, sintáticos e semânticos Prof. Nemésio Freitas Duarte FilhoRoteiro
Aula - Parte 1
Léxicos
Erros Sintáticos
Introdução
Em geral, programas compilados contém erros.
Um bom compilador deve, na medida do possível,
detectar todos os erros ocorridos, avisar o usuário
das ocorrências (sendo o mais preciso que puder).
E mais importante, recuperar-se deles de modo a
poder analisar o programa fonte até o fim.
Um bom mecanismo de detecção e recuperação
de erros só pode ser implementado em um
compilador se for considerado desde o início do
projeto da linguagem.
ERROS
Na ocorrência de um erro, um compilador pode
apresentar comportamentos distintos:
dar uma pane e travar a máquina OU gerar código objeto errado OU abortar a compilação, são comportamentos absolutamente inaceitáveis;
detectar e acusar o erro da forma mais precisa possível e recuperar-se para poder continuar a compilação até o fim (pelo menos com a análise léxica, sintática e semântica);
ERROS
reparar o erro, transformando um símbolo de entrada errado em algo similar que seja aceitável (p.ex. ao ver 2 = 3 * 4 acusa o erro de atribuição inválida, substitui o 2 por um identificador virtual e continua a análise);
corrigir o erro tentando prever (adivinhar) o objetivo do usuário de modo a continuar a compilação e geração de código para produzir um módulo executável (onde alguns erros de execução possam ser observados).
Por exemplo, ao ver IF A > B C = D, acusa o erro de falta de THEN, insere o símbolo automaticamente e segue em frente.
ERROS
Poucos compiladores fazem reparos de erros e
menos ainda fazem correção.
Nenhum compilador pode adivinhar com absoluta
certeza
os
objetivos
do
programador,
particularmente em um programa com vários
erros.
Um exemplo de compilador que tem correção de
ACUSANDO ERROS
Para ajudar (e não atrapalhar mais ainda) o
programador, as mensagens de erro devem:
mostrar o erro exatamente (ou o mais próximo possível) onde ocorreu em relação ao arquivo fonte do programa;
ser significativas e compreensivas para o usuário (p.ex. "falta fecha parênteses na linha 5" é melhor que "5: erro RB04");
ACUSANDO ERROS
ser específicas e indicadoras do problema (p.ex. "variável SOMA não declarada na função SOMATÓRIO" ao invés de "identificador não declarado";
não ser redundantes (p.ex. se a variável SOMA não for declarada, isso deve ser dito na primeira, e não em toda, ocorrência de SOMA no programa fonte).
ERROS LÉXICOS
Os erros léxicos (de grafia de símbolos) são os mais fáceis de
detectar e corrigir. O analisador léxico deve, da melhor maneira possível, recuperar-se deles sem causar reflexos para o analisador sintático/semântico.
ERROS SINTÁTICOS
Os erros sintáticos (da estrutura da linguagem) são os mais
freqüentes e mais difíceis de recuperar. Podem ser agrupados em:
Tratamento de erros sintáticos
Funções do tratamento de erros
Deve relatar a presença de erros de forma clara e precisa Deve se recuperar de cada erro para continuar a análise
do programa
Pode reparar alguns erros
Erro sintático
Programa não condiz com a gramática da linguagem:
símbolo esperado não encontrado
A realização efetiva do tratamento de erros pode ser
Tratamento de erros sintáticos
Felizmente, a maioria dos erros são simples
Pesquisa com estudantes de Pascal
80% dos enunciados contém apenas um erro; 13% tem dois
90% são erros em um único token
60% são erros de pontuação: p.ex., uso do ponto e vírgula (;)
20% são erros de operadores e operandos: p.ex., omissão de : no símbolo :=
15% são erros de palavras-chave: p. ex., erros ortográficos (wrteln)
Tratamento de erros sintáticos
O tratamento inadequado de erros pode
introduzir uma avalanche de erros espúrios,
que não foram cometidos pelo programador,
mas pelo tratamento de erros realizado
Tratamento de erros deve ser cauteloso e
selecionar os tipos de erros que podem ser
tratados para se obter um processamento
eficiente
Tratamento de erros sintáticos
Muitas técnicas para o tratamento de erros
Nenhuma delas se mostrou universalmente aceitável
Poucos métodos têm ampla aplicabilidade
Muitas vezes é artesanal
Estratégias de tratamento de erro
Modo de pânico
Recuperação de frases
Produções de erro
Tratamento de erros sintáticos
Modo de pânico
Método mais simples e fácil de implementar; usado pela
maioria dos analisadores sintáticos
Ao encontrar um erro
1. Relata-se o erro
2. Pulam-se tokens até que um token de sincronização seja
encontrado
Tokens de sincronização: pontos do programa (palavras-chave,
delimitadores, etc.) em que é possível se retomar a análise sintática com certa segurança;
esses tokens precisam ser determinados pelo projetista do compilador
Exemplo
while (x<2 do read(y)...
Ao notar a falta do parênteses, relata-se a falta do mesmo e se consome tudo até que o token
read seja encontrado, a partir de onde se
ASD preditiva: tratamento de erros
sintáticos
Exemplo
<comandos>::=<comando><mais_comandos> <mais_comandos>::=;<comandos>| ε <comando> ::= read... | write... |while (<condição>) do <comandos> $ | if...
Se acontecer um erro dentro de <condição>, buscam-se seus seguidores para se retomar a análise:
)
, por exemploASD preditiva: tratamento de erros
sintáticos
Modo de pânico: pulam-se tokens até que se
encontre um a partir do qual se possa retomar a
análise
program Pvar
x: integer begin ... end.Diante da ausência/erro de var, de onde recomeçar a análise? Quem é esse símbolo de recomeço?
ASD preditiva: tratamento de erros
sintáticos
Modo de pânico: pulam-se tokens até que se encontre
um a partir do qual se possa retomar a análise
program P
var
x:
integer begin... end.
Diante da ausência/erro de um trecho grande de código, de onde recomeçar a análise? Quem é esse símbolo de
recomeço?
ASD preditiva: tratamento de erros
sintáticos
Modo de pânico: pulam-se tokens até que se encontre
um a partir do qual se possa retomar a análise
program P ... begin read(x)
;
write(x+2); ... end.Diante da ausência/erro do ponto e vírgula (;), de onde
recomeçar a análise? Quem é esse símbolo de recomeço? Símbolo de write, primeiro de comando
Tratamento de erros sintáticos
Recuperação de frases (correção local)
Ao se detectar um erro, realizam-se
correções locais na entrada
Por exemplo, substituição de vírgula por ponto
e vírgula, remover um ponto e vírgula
estranho
Deve existir um planejamento das correções
Tratamento de erros sintáticos
Produções de erro
Com um bom conhecimento dos erros que podem ser
cometidos, pode-se aumentar a gramática da linguagem com produções para reconhecer as produções ilegais e tratá-las adequadamente
Correção global
Método muito custoso de se implementar; apenas de
interesse teórico
Idealmente, o compilador deveria fazer tão poucas
modificações no programa quanto possível
Escolhe-se uma seqüência mínima de modificações no
programa que o tornem correto com o menor custo possível
ERROS SEMÂNTICOS
A fonte principal de erros semânticos é a falta de declaração
de identificadores e a incompatibilidade de tipos.
Uma vez detectado um identificador não declarado, uma
mensagem de erro deve ser emitida e providências devem ser tomadas para que não se emita a mesma mensagem para cada nova ocorrência do mesmo identificador.
ERROS SEMÂNTICOS
Uma boa forma de se fazer isso é incluir na tabela de
símbolos o identificador não declarado, assinalando-se na mesma que o identificador foi incluído automaticamente para evitar novas mensagens de erro (e não por ter sido declarado corretamente).
Os atributos do identificador podem ser definidos
analisando-se o contexto onde ele aparece (p.ex. se aparece no meio de uma expressão inteira, é razoável supor que se trata de um identificador inteiro).
Análise e Recuperação de Erros
Recuperação de Erros
Quando um compilador detecta um erro de sintaxe, é
desejável que ele tente continuar o processo de análise de modo a detectar outros erros que possam existir no código ainda não analisado
Tarefa de Análise e Reparação (ou recuperação) de erros.
Isso é bom para o programador que terá como resultado de
Recuperação de Erros
Na reparação de erros, tenta-se colocar o analisador em um
estado tal que o restante da sentença de entrada possa ser analisada.
Esse processo pode envolver modificação da pilha de
Exemplo de Recuperação de Erros
O compilador ao ver
2 = 3 * 4
Acusa o erro de atribuição inválida, substitui o 2 por um
Exemplo de Recuperação de Erros
Tentando corrigir o erro (mais difícil)
Ao ver IF A > B C = D
Acusa o erro de falta de THEN, insere o símbolo
Erros em Programas
A maioria das especificações de linguagens de programação
não descreve como um compilador deveria responder aos erros.
A correção der erros é deixada aos projetistas dos
compiladores.
O planejamento do tratamento de erros exatamente desde
o início poderia:
simplificar a estrutura de um compilador melhorar sua resposta aos erros
Tipos de Erros
Em um programa podem ocorrer erros em vários
níveis diferentes, exemplos:
Léxicos: Errar na grafia de um identificador, palavra chave ou operador.
Sintáticos: Uma expressão aritmética com parênteses não balanceados.
Semânticos: Um operador aplicado à um operando incompatível.
Erros e Análise Sintática
Boa parte da detecção e recuperação de erros num
compilador está na fase de Análise Sintática.
Isso porque os erros ou são sintáticos por natureza ou são
expostos quando o fluxo de tokens vindo do Analisador Léxico desobedece às regras gramaticais que definem a linguagem de programação.
Tratamento de Erros
O tratador de erros num analisador sintático possui metas
simples de serem estabelecidas:
Relatar a presença de erros clara e apuradamente.
Recuperar-se de cada erro suficientemente rápido a fim de ser capaz de detectar erros na sequência.
Não retardar significativamente o processamento de programas corretos.
Estatísticas dos Erros
Alguns estudiosos anotaram os tipos de erros que ocorrem na prática com alunos experientes:
Muitos dos erros podiam ser classificados como:erros de pontuação. erros de operadores e operandos.
erros de palavras-chave outros tipos de erros.
Tratamento de Erros
Na ocorrência de um erro o compilador deve, no mínimo, informar:
Local do programa fonte onde foi detectado;Linha, função. Uma mensagem do tipo de erro.
Alguns compiladores podem inclusive tentar
“adivinhar” o que o programador queria digitar e
corrigir automaticamente, entretanto isso pode
acabar inserindo ainda mais erros.
Recuperação de Erros
Existem muitas estratégias que um analisador
sintático pode empregar para se recuperar de um
erro, por exemplo:
Modalidade do desespero (panic mode); Nível de fase;
Produções de erro; e Correção global.
Modalidade do Desespero
“Panic mode”
Ao descobrir um erro, o analisador sintático descarta
símbolos de entrada, um de cada vez, até que seja
encontrado um token pertencente a um conjunto de
“tokens de sincronização”, que normalmente são os
delimitadores tais como o “;” e o “end”.
Os tokens de sincronização são definidos pelo projetista.
Uma vez encontrado o erro o compilador pode saltar
essa parte da entrada e continuar procurando por erros
adicionais.
Recuperação de Fases
Ao descobrir um erro, o analisador sintático
pode realizar uma correção local na
entrada restante, isto é, substituir um
prefixo da entrada por alguma cadeia que
permita ao analisador seguir em frente.
Correções locais típicas:
Substituir uma vírgula por ponto e vírgula;
Remover um ponto e vírgula estranho ou
Produção de Erros
Quando se tem uma boa idéia dos erros
comuns que poderiam ser encontrados, é
possível aumentar a gramática para a
linguagem com regras de produções de erro.
Com essas regras pode-se gerar diagnósticos
apropriados para indicar a construção ilegal
que foi reconhecida na entrada.
Correção Global
Tenta calcular a sequencia mínima de mudanças
para a correção de um erro. Essa estratégia tem
apenas interesse teórico.
O compilador ideal faz tão poucas mudanças quanto
possível, ao processar uma cadeia de entrada ilegal.
Existem algoritmos para escolher uma sequencia
mínima de mudanças de forma a se obter uma
correção global de menor custo.
Correção Global
Dadas uma cadeia de entrada incorreta X e
uma gramática G, esses algoritmos irão
encontrar uma árvore gramatical para uma
cadeia relacionada Y, de tal forma que as
inserções, remoções e mudanças de tokens
necessárias para transformar X em Y sejam
tão pequenas quanto possível.
A melhor forma de acusar erros -
Resumo
Para ajudar (e não atrapalhar mais ainda) o
programador, as mensagens de erro devem:
mostrar o erro exatamente (ou o mais próximo
possível) onde ocorreu em relação ao arquivo fonte do
programa;
ser significativas para o usuário
"falta fechaA melhor forma de acusar erros -
Resumo
ser específicas e indicadoras do problema “variável SOMA não declarada na função SOMATÓRIO” ao invés de “identificador não declarado”
não ser redundantes se a variável SOMA não for declarada, isso deve ser dito na primeira, e não em toda, ocorrência de SOMA no programa fonte.
Algumas forma de se (tentar) corrigir
erros - Resumo
ERROS LÉXICOS
Os erros léxicos (de grafia de símbolos) são os mais fáceis de detectar e corrigir.
O analisador léxico deve, da melhor maneira possível, recuperar-se deles sem causar reflexos para o analisador sintático/semântico.
Algumas forma de se (tentar) corrigir
erros – Léxicos - Resumo
Identificador com mais caracteres que o permitido
léxico deve ler todo o identificador, acusar o erro, truncar o
excesso de caracteres e entregar o símbolo truncado p/ o sintático
Constante numérica maior (menor) que o limite
superior (inferior) da linguagem
léxico deve ler toda a constante, acusar o erro, mudar o valor p/ o
limite superior (inferior) e entregar o símbolo ajustado p/ o sintático
Constante alfanumérica maior que o permitido
tratamento semelhante ao de identificadorAlgumas forma de se (tentar) corrigir
erros - Resumo
ERROS SINTÁTICOS
Os erros sintáticos (da estrutura da linguagem)
são os mais frequentes e mais difíceis de
recuperar.
Algumas forma de se (tentar) corrigir
erros – Sintáticos - Resumo
Inserção de caractere/símbolo estranho SOMA = PARC_1 + + PARC_2
/* operador + duplicado */
Remoção de caractere/símbolo necessário SOMA = PARC_1 PARC_2
/* operador + faltando */
Troca de caractere/símbolo correto por caractere/ símbolo incorreto FOR I := 1 UNTIL 10
/* UNTIL usado no lugar de TO */
Transposição de 2 caracteres/símbolos adjacentes WIHLE I < 10 DO
Algumas forma de se (tentar) corrigir
erros - Resumo
A maioria dos erros semânticos é causada pela
falta de declaração de identificadores e a
incompatibilidade de tipos.
Dúvidas
E-mail:
nemesiofreitas@gmail.com
Site: